package javaapplication1;

import java.util.ArrayList;
import java.util.Random;

/**
 * User: VU MINH KHOI
 * Date: April 4, 2011
 * CP1300 Assignment 1
 */
public class GridView {
    private String[][] cell;
    private ProjModel pm;    
    private int horInterval=0; //The number of cycle for a new car appears
    private int verInterval=0;
    private ArrayList<Vehicle> vehicles = new ArrayList<Vehicle>();
    private TrafficLight verLight; //Traffic light on vertical lane
    private TrafficLight horLight; //Traffic light on horizontal lane
    private int countSimulation=0;

    public GridView() {
    }


    public GridView(ProjModel pm) {
        this.pm = pm;

        //init traffic lights
        verLight = new TrafficLight(0, this.pm.getChangingNormalLightTime(), this.pm.getChangingYellowLightTime());
        horLight = new TrafficLight(2, this.pm.getChangingNormalLightTime(), this.pm.getChangingYellowLightTime());
        this.setTrafficLightPosition();

        //init cell
        int actualHSize = pm.getSIZE() + pm.getVerLanes();
        int actualVSize = pm.getSIZE() + pm.getHorLanes();
        cell = new String[actualVSize][actualHSize];

        this.createNewCars();
        this.initGV();
    }

    private void initGV() {
        for(int i=0; i < cell.length ; i++){
            for(int k=0; k < cell[i].length ; k++){
                //cars zone
                if((k >= pm.getSIZE()/2 && k<pm.getSIZE()/2+pm.getVerLanes()) || (i >= pm.getSIZE()/2 && i<pm.getSIZE()/2+pm.getHorLanes())){
                    cell[i][k] = " ";
                }
                else
                    cell[i][k] = "-";
            }
        }
    }

    public void simulateACycle(){
        this.initGV();
        this.addTrafficLightToMap();
        this.updateCarsPosition();
        this.addCarsToMap();
        this.drawMap();
        countSimulation++;
    }

    private void drawMap(){
        System.out.println("?????? Cycle "+(countSimulation+1)+" ??????");
        for(int i=0; i < cell.length ; i++){
            System.out.print("\n");
            for(int k=0; k < cell[i].length ; k++){
                System.out.print(cell[i][k]);
            }
        }
        System.out.print("\n\n");
        this.horInterval--;
        this.verInterval--;
    }

    private void updateCarsPosition() {
        if(countSimulation==0) //init cars for the first cycle
            this.initCars();
        else{
            this.setNewPositionToCurrenCars();
            this.createNewCars();
        }
    }

    private void createNewCars() {
        if(verInterval==0){
            verInterval = (int)(1/pm.getProbVLanes()); //i.e 0.5 = 2 cycles, a new car appears
            this.createNewCarOnLane(0);
        }
        if(horInterval==0){
            horInterval = (int)(1/pm.getProbHLanes());
            this.createNewCarOnLane(1);
        }
    }

    private void initCars() {
        this.vehicles.clear();
        Vehicle vehicle1 = new Car(pm.getSIZE(), true, pm.getVerLanes(), pm.getHorLanes(),pm.getCAR_SPEED());
        this.vehicles.add(vehicle1);
        Vehicle vehicle2 = new Bicycle(pm.getSIZE(), false, pm.getVerLanes(), pm.getHorLanes(),pm.getCAR_SPEED());
        this.vehicles.add(vehicle2);
    }

    private void setNewPositionToCurrenCars() {
        for(int i=0; i < vehicles.size();i++){
            /*
             * A car will move when it DOES NOT meet both conditions:
             * 1. Traffic light is red on lane the car running on
             * 2. It is front of trafiic light
             */
            if(!((vehicles.get(i).isIsOnHLane() && horLight.getState().equals("R") && vehicles.get(i).getxPos()<= horLight.getxPos()) ||
                 (vehicles.get(i).isIsOnHLane()==false && verLight.getState().equals("R") && vehicles.get(i).getyPos()<= verLight.getyPos())))
            {
                //if traffic light is yellow, cars on that lane run at 1/2 speed
                if((vehicles.get(i).isIsOnHLane() && horLight.getState().equals("Y")) ||
                   (vehicles.get(i).isIsOnHLane()==false && verLight.getState().equals("Y")))
                    vehicles.get(i).move(0.5);
                //If not, run at normal speed
                else
                    vehicles.get(i).move(1.0);
            }
        }

        //A car over-takes if 2 cars in the same position
        for(int i=0; i < vehicles.size();i++){
            for(int k=i+1; k < vehicles.size();k++){
                if(vehicles.get(i).getxPos()==vehicles.get(k).getxPos() && vehicles.get(i).getyPos()==vehicles.get(k).getyPos()){
                    vehicles.get(k).overtake(); //the car appear later will over-take
                }
            }
        }
    }

    private void createNewCarOnLane(int i) { //0: in vertical lane, 1: in horizontal lane
        Random rand = new Random();
        int number = rand.nextInt(2);
        boolean isHLane=false;
        if(i==1)
            isHLane=true;

        Vehicle vehicle;
        if(number==0)
            vehicle = new Car(pm.getSIZE(), isHLane, pm.getVerLanes(), pm.getHorLanes(),pm.getCAR_SPEED());
        else
            vehicle = new Bicycle(pm.getSIZE(), isHLane, pm.getVerLanes(), pm.getHorLanes(),pm.getCAR_SPEED());

        this.vehicles.add(vehicle);
    }

    private void addCarsToMap() {
        for(int i=0; i < vehicles.size();i++){
            //if cars move out of maps, remove them
            if(vehicles.get(i).getyPos()>this.cell.length-1 || vehicles.get(i).getxPos()>this.cell[0].length-1)
                vehicles.remove(i);
            //if not, set it to the map cell
            else
                this.cell[vehicles.get(i).getyPos()][vehicles.get(i).getxPos()] = vehicles.get(i).getShape();
        }
    }

    private void addTrafficLightToMap() {
        this.cell[verLight.getyPos()][verLight.getxPos()]=verLight.changeState();
        this.cell[horLight.getyPos()][horLight.getxPos()]=horLight.changeState();
    }

    private void setTrafficLightPosition() {
        verLight.setPosition(this.pm.getSIZE()/2-1,this.pm.getSIZE()/2-1);
        horLight.setPosition(this.pm.getSIZE()/2+this.pm.getHorLanes(),this.pm.getSIZE()/2-1);
    }
}

